﻿

namespace System.Extensions.IO
{
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  using System.Text;
  using System.Threading;
  using System.Threading.Tasks;

  /// <summary>
  /// Dictionary class to store <see cref="IniFileSection"/> instances keyed by name.
  /// </summary>
  public class IniFileSectionCollection : KeyedCollection<string, IniFileSection>, IDirty
  {
    private bool dirty;

    /// <summary>
    /// Initializes a new instance of the <see cref="IniFileSectionCollection"/> class.
    /// </summary>
    public IniFileSectionCollection()
      : base()
    {
    }

    /// <summary>
    /// Gets whether the <see cref="IniFileSectionCollection"/> is dirty.
    /// </summary>
    public bool IsDirty
    {
      get
      {
        if (this.dirty ||
            new List<IniFileSection>(DirtySections()).Count > 0)
        {
          return true;
        }
        else
        {
          return false;
        }
      }

      internal set
      {
        this.SetDirtyState(value);
      }
    }

    private void SetDirtyState(bool state)
    {
      this.dirty = state;
      foreach (IniFileSection section in this)
      {
        section.IsDirty = state;
      }
    }

    /// <summary>
    /// Inserts an item into the collection at the specified index.
    /// </summary>
    protected override void InsertItem(int index, IniFileSection item)
    {
      base.InsertItem(index, item);
      this.dirty = true;
    }

    /// <summary>
    /// Removes an element from the collection at the specified index.
    /// </summary>
    protected override void RemoveItem(int index)
    {
      base.RemoveItem(index);
      this.dirty = true;
    }

    /// <summary>
    /// Removes all elements from the collection.
    /// </summary>
    protected override void ClearItems()
    {
      base.ClearItems();
      this.dirty = true;
    }

    /// <summary>
    /// Replaces the item at the specified index with the specified item.
    /// </summary>
    protected override void SetItem(int index, IniFileSection item)
    {
      base.SetItem(index, item);
      this.dirty = true;
    }

    /// <summary>
    /// Returns the string represenation of this collection of sections.
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
      StringBuilder sb = new StringBuilder();
      foreach (IniFileSection section in this)
      {
        sb.Append(section.ToString());
      }

      return sb.ToString();
    }

    private IEnumerable<IniFileSection> DirtySections()
    {
      return Filter<IniFileSection>(this, new Predicate<IniFileSection>(this.IsSectionDirty));
    }

    private IEnumerable<IniFileSection> Sections(string name)
    {
      return Filter<IniFileSection>(this, new PredicateSectionName<IniFileSection>(this.IsSectionName), name);
    }

    private bool IsSectionDirty(IniFileSection section)
    {
      return section.IsDirty;
    }

    private bool IsSectionName(IniFileSection section, string name)
    {
      return section.Name.Equals(name);
    }

    private delegate bool Predicate<T>(T inputValue);

    private IEnumerable<T> Filter<T>(IEnumerable<T> list, Predicate<T> condition)
    {
      foreach (T item in list)
      {
        if (condition(item))
        {
          yield return item;
        }
      }
    }

    private delegate bool PredicateSectionName<T>(T inputValue, string name);

    private IEnumerable<T> Filter<T>(IEnumerable<T> list, PredicateSectionName<T> condition, string name)
    {
      foreach (T item in list)
      {
        if (condition(item, name))
        {
          yield return item;
        }
      }
    }

    /// <summary>
    /// Gets the key to use for the section.
    /// </summary>
    protected override string GetKeyForItem(IniFileSection item)
    {
      return item.Name;
    }
  }
}
